﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using Voodoo.TestData.Strategy;
using Voodoo.TestData.Strategy.NameStrategy;
using Voodoo.TestData.Strategy.TypeStrategy;

namespace Voodoo.TestData
{
    public class Randomizer
    {
        protected List<GenerationStrategy> TypeStrategies { get; set; }
        protected List<GenerationByNameStrategy> NameStrategies { get; set; }

        public Randomizer()
        {
            TypeStrategies = new List<GenerationStrategy>();
            NameStrategies = new List<GenerationByNameStrategy>();

            NameStrategies.Add(new PropertyEndsWithIdNameStrategy());


            TypeStrategies.Add(new ByteStrategy());
            TypeStrategies.Add(new IntStrategy());
            TypeStrategies.Add(new ShortStrategy());
            TypeStrategies.Add(new LongStrategy());
            TypeStrategies.Add(new SingleStrategy());
            TypeStrategies.Add(new DoubleStrategy());
            TypeStrategies.Add(new DecimalStrategy());
            TypeStrategies.Add(new DateStrategy());
            TypeStrategies.Add(new BoolStrategy());
            TypeStrategies.Add(new DefaultStringGenerationStrategy());
        }

        public void AddOrReplaceTypeStrategy<T>(GenerationByTypeStrategy<T> strategy)
        {
            var targetType = typeof(T);
            var strategies = TypeStrategies.Where(c => c.Type != targetType).ToList();
            strategies.Add(strategy);
            TypeStrategies = strategies;
        }

        public void AddOrReplaceNameStrategy<T>(GenerationByNameStrategy<T> strategy)
        {
            var targetType = typeof(T);
            var strategies = NameStrategies.Where(c => c.Type != targetType).ToList();
            strategies.Add(strategy);
            NameStrategies = strategies;
        }


        public void Randomize(object @object)
        {

            DefaultStringGenerationStrategy stringStrategy = null;
            var existing = TypeStrategies.Where(c => c is DefaultStringGenerationStrategy).FirstOrDefault();
            if (existing != null)
            {
                stringStrategy = (existing as DefaultStringGenerationStrategy);
                stringStrategy.Person = TestHelper.Data.Person();
                AddOrReplaceTypeStrategy<string>(stringStrategy);
            }

            var type = @object.GetType();

            var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);

            foreach (PropertyInfo info in properties)
            {
                if (!info.CanWrite) { continue; }
                var propertyType = info.PropertyType;
                if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
                {
                    propertyType = propertyType.GetGenericArguments()[0];
                }

                var nameStrategy =
                        NameStrategies
                        .Where(strategy =>
                                strategy.Matches(info) ||
                                strategy.Matches(propertyType) ||
                                strategy.Matches(propertyType, info))
                        .OrderBy(c => c.SortOrder)
                        .FirstOrDefault();

                if (nameStrategy != null)
                {
                    nameStrategy.SetValue(@object, info);
                    continue;
                }

                var typeStrategy = TypeStrategies
                        .Where(c => c.Type == propertyType)
                        .FirstOrDefault();

                if (typeStrategy != null)
                {                   
                    typeStrategy.SetValue(@object, info);
                    continue;
                }
            }
        }
    }
}
